import typing
from datetime import date, time, datetime, timedelta
from uuid import UUID

from strange_io.object import StrangeObject

def serialize(obj: dict) -> StrangeObject:
    sobj = StrangeObject()
    output = {}
    for key in obj:
        if type(key) != str:
            raise ValueError()
        value = obj[key]
        output[key] = _serialize(value)
    sobj.load(output)
    return sobj

def _serialize(obj):
    if type(obj) == int:
        return {'type': 'int', 'value': obj}
    if type(obj) == float:
        return {'type': 'float', 'value': obj}
    if type(obj) == str:
        return {'type': 'str', 'value': obj}
    if type(obj) == bytes:
        return {'type': 'bytes', 'value': obj}
    if type(obj) == bool:
        return {'type': 'bool', 'value': obj}
    if type(obj) == bytearray:
        return {'type': 'bytearray', 'value': bytes(obj)}
    if type(obj) == list:
        return {'type': 'list', 'value': [_serialize(i) for i in obj]}
    if type(obj) == tuple:
        return {'type': 'tuple', 'value': [_serialize(i) for i in obj]}
    if type(obj) == set:
        return {'type': 'set', 'value': [_serialize(i) for i in obj]}
    if type(obj) == dict:
        value = []
        for k in obj:
            v = obj[k]
            value.append({
                'key': _serialize(k),
                'value': _serialize(v)
            })
        return {'type': 'dict', 'value': value}
    if type(obj) == UUID:
        return {'type': 'uuid', 'value': str(obj)}
    if type(obj) == date:
        return {'type': 'date', 'value': [obj.year, obj.month, obj.day]}
    if type(obj) == time:
        return {'type': 'time', 'value': [obj.hour, obj.minute, obj.second, obj.microsecond]}
    if type(obj) == datetime:
        return {'type': 'datetime', 'value': [obj.year, obj.month, obj.day, obj.hour, obj.minute, obj.second, obj.microsecond]}
    if type(obj) == timedelta:
        return {'type': 'timedelta', 'value': [obj.days, obj.seconds, obj.microseconds]}
    return {'type': 'null'}

def deserialize(sobj: StrangeObject) -> dict:
    output = {}
    obj = sobj.dump()
    for key in obj:
        output[key] = _deserialize(obj[key])
    return output

def _deserialize(obj: dict[str, typing.Any]):
    if obj['type'] in ['int', 'float', 'str', 'bytes', 'bool']:
        return obj['value']
    if obj['type'] == 'bytearray':
        return bytearray(obj['value'])
    if obj['type'] == 'list':
        return list([_deserialize(i) for i in obj['value']])
    if obj['type'] == 'tuple':
        return tuple([_deserialize(i) for i in obj['value']])
    if obj['type'] == 'set':
        return set([_deserialize(i) for i in obj['value']])
    if obj['type'] == 'uuid':
        return UUID(obj['value'])
    if obj['type'] == 'date':
        return date(*obj['value'])
    if obj['type'] == 'time':
        return time(*obj['value'])
    if obj['type'] == 'datetime':
        return datetime(*obj['value'])
    if obj['type'] == 'timedelta':
        return timedelta(days=obj['value'][0], seconds=obj['value'][1], microseconds=obj['value'][2])
    if obj['type'] == 'dict':
        return {_deserialize(i['key']): _deserialize(i['value']) for i in obj['value']}
    return None
